package com.webserver.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 *  请求对象
 *  当前类的每一个实例用于表示HTTP协议要求的客户端(浏览器)发送过来的一个请求内容
 *  每个请求由三部分构成:
 *  请求行,消息头,消息正文
 */
public class HttpRequest {
    //请求行相关信息
    private String method;//保存请求行中的请求方式
    private String uri;//保存抽象路径
    private String protocol;//保存协议版本

    private String requestURI;//存uri中请求部分,"?"左侧内容
    private String queryString;//存uri中参数部分,"?"右侧内容
    private Map<String,String> parameters = new HashMap<>();//存每一组参数

    //消息头相关信息
    private Map<String,String> headers = new HashMap<>();

    //消息正文相关信息

    private Socket socket;

    public HttpRequest(Socket socket) throws IOException, EmptyRequestException {
        this.socket = socket;
        //1解析请求行
        parseRequestLine();
        //2解析消息头
        parseHeaders();
        //3解析消息正文
        parseContent();
    }

    private void parseRequestLine() throws IOException, EmptyRequestException {
        String line = readLine();
        if (line.isEmpty()){//空请求!
            throw new EmptyRequestException("空请求!");
        }
        System.out.println("请求行:" + line);

        String[] data = line.split("\\s");//按照空格拆分
        method = data[0];
        uri = data[1];
        protocol = data[2];

        //进一步解析uri
        parseUri();

        System.out.println("method:" + method);
        System.out.println("uri:" + uri);
        System.out.println("protocol:" + protocol);
        System.out.println("requestURI" + requestURI);
        System.out.println("parameters" + parameters);
    }

    /**
     *  进一步解析uri,将请求与参数拆分
     */
    private void parseUri(){
        /*
            uri可能存在两种情况:
            1.不带参数
              例:/myweb/reg.html
              这种不带参数的就相当于是纯请求部分,我们直接将uri的值赋给requestURI这个属性即可

            2.带参数
            例:/myweb/regUser?username=xxx&password=xxx&nickname=xx......
            首先我们将uri按照"?"拆分为请求部分与参数部分,并将请求部分赋值给requestURI属性,
            将参数部分赋值给queryString属性.然后再对参数部分进一步拆分,首先按照"&"拆分出参数部分中的每一组参数,
            然后再将每组参数按照"="拆分为参数名与参数值,并将参数名作为key,参数值作为value保存到parameters这个Map中即可.
         */
        if (uri.contains("?")){
            String[] c1 = uri.split("\\?");
            requestURI = c1[0];//左侧给requestURI
            if (c1.length>1) {//"?"后是否有内容
                queryString = c1[1];//右侧给queryString
                String[] c2 = queryString.split("&");
                for (String pa : c2) {
                    String[] c3 = pa.split("=");//拆分为name=zhang
                    if (c3.length > 1) {//等号后有内容
                        parameters.put(c3[0], c3[1]);
                    } else {//等号后没内容
                        parameters.put(c3[0], null);
                    }
                }
            }
        }else {
            requestURI = uri;
        }
    }

    private void parseHeaders() throws IOException {
        while (true){
            String line = readLine();
            if (line.isEmpty()){//判断是否为空字符串,是的话说明读到了CRLF
                break;
            }
            System.out.println("消息头:"+line);
            String[] s = line.split(":\\s");
            headers.put(s[0],s[1]);
        }
        System.out.println(headers);
    }

    private void parseContent(){}

    private String readLine() throws IOException {
        /*
            InputStream get InputStream
            该方法获取的输入流用于读取远端计算机发送过来的数据.
            注意:Socket不变的前提下,无论调用多少次该方法,返回的输入流始终是同一个流.
         */
        InputStream in = socket.getInputStream();
        int d;
        char pre = 'a';//记录上次读取的字符内容
        char cur;      //记录本次读取的字符内容
        StringBuilder builder = new StringBuilder();
        while ((d = in.read()) != -1) {
            cur = (char) d;
            if (pre == 13 && cur == 10) {
                break;
            }
            builder.append(cur);
            pre = cur;//将本次读取的字符内容在下次循环时记录在上次读取的字符
        }
        return builder.toString().trim();
    }

    public String getMethod() {
        return method;
    }

    public String getUri() {
        return uri;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getHeader(String name){
        return headers.get(name);
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getQueryString() {
        return queryString;
    }

    public String getParameter(String name){
        return parameters.get(name);
    }
}